﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using Xceed.Silverlight.Compatibility;

namespace Xceed.Silverlight.Data
{
  internal class RawDataItem : DataItem
  {
    #region Static Fields

    private static readonly object SyncRoot = new object();
    private static readonly IDictionary<Tuple<Type, string>, Func<object, object>> PropertyGetters = new Dictionary<Tuple<Type, string>, Func<object, object>>();

    #endregion

    internal RawDataItem( object data )
      : base( data )
    {
    }

    #region IsDeleted Property

    internal bool IsDeleted
    {
      get
      {
        return m_isDeleted;
      }
    }

    private bool m_isDeleted; //false

    #endregion

    public override int GetHashCode()
    {
      return base.GetHashCode();
    }

    public override bool Equals( object obj )
    {
      if( object.ReferenceEquals( obj, null ) )
        return false;

      if( object.ReferenceEquals( obj, this ) )
        return true;

      if( !( obj is RawDataItem ) )
        return false;

      return base.Equals( obj );
    }

    protected virtual bool FindDataMemberValue( string memberName, out object value )
    {
      value = null;

      object data = this.Data;

      if( data == null )
        return true;

      Func<object, object> getter = null;
      Type dataType = data.GetType();

      lock( RawDataItem.SyncRoot )
      {
        Tuple<Type, string> key = new Tuple<Type, string>( dataType, memberName );

        if( !RawDataItem.PropertyGetters.TryGetValue( key, out getter ) )
        {
          getter = this.CreatePropertyGetter( dataType, memberName );

          if( getter != null )
          {
            // Intern the member name to improve string comparison performance
            // on future call to this method.
            memberName = string.Intern( memberName );

            // The key must be rebuild to use the interned string.
            key = new Tuple<Type, string>( dataType, memberName );

            RawDataItem.PropertyGetters.Add( key, getter );
          }
        }
      }

      if( getter == null )
        return false;

      value = getter.Invoke( data );

      return true;
    }

    internal object GetDataMemberValue( string memberName )
    {
      if( memberName == null )
        throw new ArgumentNullException( "memberName" );

      object value;

      if( this.FindDataMemberValue( memberName, out value ) )
        return value;

      throw new ArgumentException( "The specified member name does not exist.", "memberName" );
    }

    internal bool TryGetDataMemberValue( string memberName, out object value )
    {
      value = null;

      return this.FindDataMemberValue( memberName, out value );
    }

    internal virtual void Delete()
    {
      m_isDeleted = true;
    }

    private Func<object, object> CreatePropertyGetter( Type dataType, string memberName )
    {
      // For Phone SDK Compatibility
      if( StringHelper.IsNullOrWhiteSpace( memberName ) )
        return null;

      ParameterExpression parameter = Expression.Parameter( typeof( object ), null );

      Expression body = parameter;

      if( memberName != "." )
      {
        bool isNullableExpression;
        body = ComplexStringHelper.GetExpressionFromString( memberName, dataType, body, true, out isNullableExpression );

        if( body.Type != typeof( object ) )
          body = Expression.Convert( body, typeof( object ) );
      }

      LambdaExpression lambda = Expression.Lambda( body, parameter );

      return ( Func<object, object> )lambda.Compile();
    }
  }
}
